#include "FileSystem.h"
#include "utility/e7convert.h"

#include <iostream>
#include <fstream>

using namespace std;

/**
 * implementatie gaat hier
 */

FileSystem::FileSystem(const char * device) {

	//skip boot sector
	int p = DBLKSIZ;

	int length;
	char * buffer;

	ifstream is;
	is.open(device, ios::binary | ios::in);
	if (!is) {
		cout << device << " bestaat niet\n";
		return;
	}
	is.seekg(0, ios::end);
	length = is.tellg();
	is.seekg(0, ios::beg);
	cout << length << "\n";
	buffer = new char[length];

	is.read(buffer, length);
	is.close();

	//FileSystem fs(data);

	byte * d = (byte*) buffer;
	data = d;
	s_isize = getshort(d, p);
	s_fsize = getlong(d, p);
	s_nfree = getshort(d, p);
	for (int i = 0; i < NICFREE; i++) {
		s_free[i] = getlong(d, p);
	}
	s_ninode = getshort(d, p);
	for (int i = 0; i < NICINOD; i++) {
		s_inode[i] = getshort(d, p);
	}

	s_flock = getbyte(d, p);
	s_ilock = getbyte(d, p);
	s_fmod = getbyte(d, p);
	s_ronly = getbyte(d, p);
	s_time = getlong(d, p);
	s_tfree = getlong(d, p);
	s_tinode = getshort(d, p);
	s_m = getshort(d, p);
	s_n = getshort(d, p);

	for (int i = 0; i < 6; i++) {
		s_fname[i] = getbyte(d, p);
	}

	for (int i = 0; i < 6; i++) {
		s_fpack[i] = getbyte(d, p);
	}

	//laad alle inodes in
	int pos = (DBLKSIZ * 2) + 64;
	for (int i = 0; i < 471; i++) {
		Inode * in = new Inode(d, pos, i + 2);
		if (in->di_size != 0) {
			inodes.push_back(in);
		}
		pos += 64;
	}
	cout << "\n\n";
	for (list<Inode*>::iterator it = inodes.begin(); it != inodes.end(); it++) {
		Inode * inode = *it;
		inode->print();
		inode->dumpToFile(d);
		if (inode->isDir()) {
			if (inode->inode_nr == 2) {
				main_dir = new Directory(inode, d);
				files.insert(make_pair(inode->inode_nr, main_dir));
			} else {
				files.insert(
						make_pair(inode->inode_nr, new Directory(inode, d)));
			}
		} else if (inode->isRegular()) {
			files.insert(make_pair(inode->inode_nr, new File(inode)));
		}
	}
}

Inode * FileSystem::findInode(const char * absolute_path) {

	string abs_path = absolute_path;
	string dir;
	int inode_nr;

	if (abs_path == "/") {
		cout << "main dir";
		inode_nr = 2;
		return main_dir->getInode();
	}

	stringstream stream(abs_path);
	Directory * current_dir = main_dir;
	vector<string> dirs;

	while (getline(stream, dir, '/')) {
		cout << dir << "/";
		dirs.push_back(dir);
	}
	cout << "\n";
	//stap door directories en check of ze allemaal bestaan
	for (int i = 0; i < dirs.size(); i++) {
		dir = dirs[i];
		if (dir != "") {
			cout << "|" << dir << "| ";
			if (current_dir->contains(dir)) {
				inode_nr = current_dir->findFile(dir);
				if (i != dirs.size() - 1)
					current_dir
							= dynamic_cast<Directory*> (files.find(inode_nr)->second);
			} else {
				//als bestand of directory niet bestaan, return 0
				return 0;
			}
		}
	}
	return files.find(inode_nr)->second->getInode();
}

Inode * FileSystem::findFile(Inode * dirptr, const char * filename) {
	if (!dirptr->isDir())
		return 0;
	Directory * dir =
			dynamic_cast<Directory*> (files.find(dirptr->inode_nr)->second);
	return files.find(dir->findFile(filename))->second->getInode();
}

void FileSystem::releaseInode(Inode * ip) {
	AIT.remove(ip);
}

OpenFile * FileSystem::open(const char * path) {

	Inode * inode = findInode(path);
	if (find(AIT.begin(), AIT.end(), inode) != AIT.end()) {
		return 0;
	}
	OpenFile * open = new OpenFile(inode, 0);

	SOFT.push_back(open);
	AIT.push_back(inode);
	return open;
}

int FileSystem::read(OpenFile * fileptr, char buffer[], int count) {
	if (count <= 0)
		return 0;

	//ga alle blokken langs totdat alles gelezen is.
	long pos = 0;
	long bytes_read = 0;
	int old_iopos = fileptr->iopos;
	for (vector<int>::iterator it = fileptr->ip->blocks.begin(); it
			!= fileptr->ip->blocks.end(); it++) {
		for (int i = 0; i < 512; i++) {
			int filesys_pos = *it * 512 + i;
			pos++;
			if (pos > fileptr->ip->di_size)
				return bytes_read;
			if (pos >= old_iopos && pos <= (old_iopos + count)) {
				bytes_read++;
				fileptr->iopos++;
				buffer[pos] = data[filesys_pos];
			}
		}
	}
	return bytes_read;

}

int FileSystem::lseek(OpenFile * fileptr, long offset, int whence) {
	switch (whence) {
	case (SEEK_CUR):
		fileptr->iopos += offset;
		if (fileptr->iopos > fileptr->ip->di_size) {
			fileptr ->iopos = fileptr->ip->di_size;
		}
		return fileptr->iopos;
		break;
	case (SEEK_SET):
		fileptr->iopos = offset;
		break;
	}
}

void FileSystem::close(OpenFile * fileptr) {
}

void FileSystem::browse() {
	int inode_nr = 2;
	Directory * current_dir = main_dir;
	string next_dir;
	while (true) {
		current_dir->print();
		cin >> next_dir;
		inode_nr = current_dir->findFile(next_dir);
		current_dir = dynamic_cast<Directory*> (files.find(inode_nr)->second);
	}

}

void FileSystem::print() {
	cout << "---------------------------------------\n";
	cout << "Dump of superblock on " << s_fname << " " << s_fpack << ":\n";
	cout << "---------------------------------------\n";
	cout << "number of blocks in i-list is: " << s_isize << "\n";
	cout << "number of blocks on volume is: " << s_fsize << "\n";
	cout << "number of freeblocks in free[] is: " << s_nfree << "\n";
	cout << "	blocks: ";
	for (int i = 0; i < s_nfree; i++)
		cout << s_free[i] << " ";
	cout << "\n";
	cout << "number of free inodes in inode[] is: " << s_ninode << "\n";
	cout << "inodes: ";
	for (int i = 0; i < s_ninode; i++)
		cout << s_inode[i] << " ";
	cout << "\n";

	cout << "freelist lock flag is " << (int) s_flock << "\n";
	cout << "i-list lock flag is " << (int) s_ilock << "\n";
	cout << "superblock is " << (int) s_fmod << "\n";
	cout << "filesystem was " << (int) s_ronly << "\n";
	cout << "Last update was " << s_time << "\n";
	cout << "total number of free blocks is: " << s_tfree << "\n";
	cout << "total number of free inodes is: " << s_tinode << "\n";
	cout << "interleave factors are: " << s_m << " and " << s_n << "\n";
	cout << "File system name is: " << s_fname << "\n";
	cout << "File system pack is: " << s_fpack << "\n";

}
